package org.abio.XasML.core
{
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	
	import org.abio.XasML.event.DependencyObjectEvent;
	import org.abio.XasML.reflect.ClassInfo;
	import org.abio.XasML.rpc.ClassLoader;
	import org.abio.XasML.rpc.Command;

	internal dynamic class LazyObject extends Proxy
	{
		private var context:Context=null;
		private var xml:XML=null;	
			
		private var classInfo:ClassInfo=null;
		private var dependencyObject:DependencyObject=null;
		
		private var bLoading:Boolean=false
		private var operation:Array=new Array();		
		
		public function LazyObject(context:Context, xml:XML)
		{
			this.context=context;
			this.xml=xml;
		}
		
		override flash_proxy function callProperty(name:*, ...rest):*{
			return this.invoke(function callProperty(name:*, ...rest):*{
				return (dependencyObject[name] as Function).apply(dependencyObject, rest);
			}, new Array(name).concat(rest));
		}
		
		override flash_proxy function getProperty(name:*):*{
			return this.invoke(function getProperty(name:*):*{
				return dependencyObject[name];
			}, [name]);
	    }
	
	    override flash_proxy function setProperty(name:*, value:*):void{
	        this.invoke(function setProperty(name:*, value:*):*{
				dependencyObject[name]=value;
			}, [name, value]);
	    }
	    
	    private function invoke(func:Function, args:Array):*{
	    	if(this.dependencyObject!=null){
	    		return func.apply(this, args);
	    	}else if(this.bLoading==true){
	    		this.operation.push(new Operation(func, args));
	    	}else{
	    		this.bLoading=true;
	    		this.operation.push(new Operation(func, args));
	    		
				var name:QName=xml.name() as QName;
				var url:String=this.context.Namespace(this.xml.namespace().prefix);
				if(url.indexOf('.*')!=-1){	url=this.context.Namespace(this.xml.namespace().prefix+'.url');}	
				if(url.indexOf('.swf')==-1){	url+='/'+name.localName+'.swf';}
				
	    		new Command(new ClassLoader(url)[name.uri.replace('*', name.localName)], function onResult(cls:ClassInfo):void{
	    			classInfo=cls;
					dependencyObject=classInfo.constructor.invoke();
					initialize();
					
					for each(var opera:Operation in operation){
						opera.func.apply(null, opera.args);
					}
					bLoading=false;
				}).execute();
	    	}
	    }
	    
	    private function initialize():void{
	    	var dobj:DependencyObject=this.dependencyObject as DependencyObject;
	    	for each(var attr:XML in this.xml.attributes()){
	    		dobj[attr.name().toString()]=attr;
	    	}
	    	
	    	this.dependencyObject.dispatchEvent(new DependencyObjectEvent(DependencyObjectEvent.INITIALIZE));
	    }
	}
}

class Operation{
	private var Func:Function=null;
	internal function get func():Function{	return this.Func;}
	private var Args:Array=null;
	internal function get args():Array{	return this.Args;}

	public function Operation(func:Function, args:Array){
		this.Func=func;
		this.Args=args;
	}
}